package parser;

import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.sun.corba.se.impl.orbutil.closure.Constant;
import com.sun.xml.internal.ws.wsdl.writer.document.Port;

import residue.AbstractResidue;
import residue.GenericComposedResidue;
import residue.GenericMonosaccharideResidue;
import residue.GenericRepeatResidue;
import residue.GenericSubstituentResidue;
import residue.Link;
import residue.iupac.composed.BacAc2;
import residue.iupac.composed.GalN;
import residue.iupac.composed.GalNAc;
import residue.iupac.composed.GlcN;
import residue.iupac.composed.GlcNAc;
import residue.iupac.composed.HexNAc;
import residue.iupac.composed.ManNAc;
import residue.iupac.composed.Neu45Ac2;
import residue.iupac.composed.Neu59Ac2;
import residue.iupac.composed.Neu5_Ac2;
import residue.iupac.composed.Neu5__Ac3;
import residue.iupac.composed.NeuAc;
import residue.iupac.composed.NeuAc2;
import residue.iupac.composed.NeuGc;
import residue.iupac.substituent.Ac;
import residue.iupac.substituent.H2po3;
import residue.iupac.substituent.Hso3;
import residue.iupac.substituent.Me;
import residue.iupac.substituent.N;
import residue.iupac.substituent.NAc;
import residue.iupac.substituent.P;
import utils.TreeTools;

public class IupacTree {
	
	private String originalSequence;
	private String sequence;
	private ArrayList<IupacBranch> branches = new ArrayList<IupacBranch>();
	private ArrayList<IupacBranch> Ctbranches = new ArrayList<IupacBranch>();
	private ArrayList<IupacBranch> CtSortedbranches= null;
	private String glycanType = null;
	private ArrayList<IupacRepeatTree> repeats = new ArrayList<IupacRepeatTree>();
	private ArrayList<IupacUndCapTree> undcaps = new ArrayList<IupacUndCapTree>();
	private String comment;
	private int ResIdx = 0 ;
	private int LinIdx = 0 ;
//	private String rootLinkage = null;
	private Link rootLink = null;
	
	/*Constructor */
	public IupacTree(String input)
	{	
		
		this.originalSequence=input;
		this.sequence=processSequence(originalSequence);
//		System.out.println("new IupacTree(): " + sequence);
	}
	public IupacTree()
	{	
	}

	
	

	/*Accessor*/
	public String getSequence() {
		return sequence;
	}

	public void setSequence(String sequence) {
		this.sequence = sequence;
	}
	
	
	public ArrayList<IupacBranch> getBranches() {
		return branches;
	}
	
	public ArrayList<IupacBranch> getCtBranches() {
		return Ctbranches;
	}
	
	public ArrayList<IupacBranch> getCtSortedbranches() {
		return CtSortedbranches;
	}
	
	public ArrayList<IupacBranch> getBranches(int level) {
		ArrayList<IupacBranch> branches_level = new ArrayList<IupacBranch>();
		
//		System.out.println("IupacTree getBranches(int level) : " + level);
		
		for(int i = 0; i< this.branches.size();i++)
		{
//			System.out.println("IupacTree getBranches() branches.size(): " + branches.size());
			if(branches.get(i).getLevel()==level)
			{
				branches_level.add( branches.get(i));
			}	
		}				
		return branches_level;
	}
	
	public ArrayList<IupacRepeatTree> getRepeats() {
		return repeats;
	}
	
	public void addToRepeats(IupacRepeatTree repeat) {
		repeats.add(repeat);
	}
	
	public ArrayList<IupacUndCapTree> getUndCaps() {
		return undcaps;
	}
	
	public void addToUndCaps(IupacUndCapTree undCap) {
		this.undcaps.add(undCap);
	}
	
	public void addToUndCaps(ArrayList<IupacUndCapTree> undCaps) {
		this.undcaps.addAll(undCaps);
	}
	
	
	public String getComment() {
		return comment;
	}


	public void setComment(String comment) {
		this.comment = comment;
	}

	public int getResIdx() {
		return ResIdx;
	}

	public void setResIdx(int resIdx) {
		ResIdx = resIdx;
	}

	public int getLinIdx() {
		return LinIdx;
	}

	public void setLinIdx(int linIdx) {
//		System.out.println("IupacTree setLinIdx : " + linIdx + " " + this.toString());
		LinIdx = linIdx;
	}
	
	
	/*
	public ArrayList<IupacBranch> getBranches(int level, String root_Id) {
		ArrayList<IupacBranch> branches_level = new ArrayList<IupacBranch>();
		
		System.out.println("IupacTree getBranches(int level) : " + level);
		
		for(int i = 0; i< this.branches.size();i++)
		{
			System.out.println("IupacTree getBranches() branches.size(): " + branches.size());
			if(branches.get(i).getLevel()==level && branches.get(i).getParentId()==root_Id)
			{
				branches_level.add( branches.get(i));
			}	
		}				
		return branches_level;
	}
	*/
	
	public String getGlycanType() {
		return glycanType;
	}

	public void setGlycanType(String glycanType) {
		this.glycanType = glycanType;
	}

	public Link getRootLink() {
		return rootLink;
	}
	public void setRootLink(Link rootLink) {
		this.rootLink = rootLink;
	}
	/*Methods */
	public void parse() throws Exception
	{
		try
		{
		//convert iupac original sequence to a iupac representation convertible to glyco_ct
		//this.sequence = processSequence(this.originalSequence);
		//split branches into linear branches
		parseBranch();
		//split linear branches into residues
		parseResidue();
		}
		
		catch(Exception ex)
		{
			throw ex;
		}
	}
	

	public String processSequence(String seq)
	{
		String processedSeq=seq;
		
		/**resolve extended name of bacillosamine*/
		processedSeq = processBac(processedSeq);
		
		/**expand composed residue in the iupac sequence*/
		processedSeq = processComposedResidues(processedSeq);
		
		/**resolve Phosphate and Phosphodiester linkage*/
		processedSeq = processP(processedSeq);
		
		/**resolve Sulphate */
		processedSeq = processS(processedSeq);
		
		/**resolve Fluorine */
		processedSeq = processF(processedSeq);
		
		/**remove Ceramide Cer*/
//		processedSeq = processCer(processedSeq);
		
//		System.out.println("IupacTree processSequence hasCommentChar: " + TreeTools.hasCommentChar(processedSeq));
//		System.out.println("IupacTree processSequence !hasRepeatChar: " + !TreeTools.hasRepeatChar(processedSeq));
		/**resolve repeats with double quotes and no curly brackets!!*/
		if(TreeTools.hasCommentChar(processedSeq) && !TreeTools.hasRepeatChar(processedSeq) && !this.isUndCapTree())
		{			
				processedSeq = processCommentUndCap(processedSeq);			
		}
//		System.out.println("IupacTree processSequence : " + processedSeq);
		/**resolve open linkage*/
		processedSeq = processOpenLinkage(processedSeq);
		
//		System.out.println("IupacTree processSequence : " + processedSeq);
		
		return processedSeq;
	}
	
	
	public String processCommentUndCap(String seq)
	{
		String processedSeq=seq;
		String comment ="";

		/*Get the comment embedded in double quotes*/
		comment = TreeTools.getComment(processedSeq);
		/*Remove the comment part*/
		processedSeq = processedSeq.replace(comment, "");
		/*Remove plus, space and double quotes*/
		processedSeq = TreeTools.cleanComment(processedSeq);
		
		/*Split comment by +*/
		String[] comments = comment.split("\\+");
		
		for(int i = 0;i < comments.length; i++)
		{
			System.out.println("comment : " + comments[i] + " "+ i + "/" +comments.length);
			/*Split comment by x*/
			this.undcaps.addAll(TreeTools.parseUndCapSeqAndMultitude(comments[i].trim(),this));
			
		}
//		System.out.println("IupacTree processCommentUndCap Und size: " + undcaps.size());
		return processedSeq;
	}
	
	
	public String processOpenLinkage(String seq)
	{
		String processedSeq=seq;
		try
		{
			String regex =	"(.+\\([ab])";
			boolean test = seq.matches(regex);
			
			if(test)//m.find())
			{
				processedSeq = processedSeq + "?-?)";
			}
//			System.out.println("IupacTree processOpenLinkage test : " + test);
//			System.out.println("IupacTree processOpenLinkage seq : " + seq);
		}
		catch(Exception ex)
		{
			System.err.println("IupacTree processOpenLinkage : " + ex.getMessage());
		}
		
//		System.out.println("IupacTree processOpenLinkage processedSeq : " + processedSeq);
		
		return processedSeq;
	}
	
	public String processP(String seq)
	{
		String processedSeq=seq;
		
		
		
	
		
		if(hasPInternal(seq))
		{
			processedSeq = processedSeq.replaceAll(Constants.P_INTERNAL, "-x)P(x-");
			
//			String regex = "(\\[?)(S)(-)([0-9|?])(\\]?)";
//			processedSeq = processedSeq.replaceAll(regex, "[$2\\(?$3$4\\)]");
			
		}
		if(hasPTerminal(seq))
		{
			processedSeq = processedSeq.replaceAll("-P\\)", "-x)P");
		}
		
		if(seq.toUpperCase().contains("H2PO3"))
		{
			processedSeq = processH2po3(processedSeq);
		}
//		System.out.println("IupacTree processP sequence : " + seq);
//		System.out.println("IupacTree processP pSequence : " + processedSeq);
		return processedSeq;
	}
	
	public String processH2po3(String seq)
	{
		String processedSeq=seq;
		
//		processedSeq.replaceAll("(?i)H2PO3", "P");
		
		try
		{
			String regex = "((?i)H2po3)(\\()(-)([0-9|?])(\\))";
			processedSeq = processedSeq.replaceAll(regex, "$1$2?$3$4$5");		
//			System.out.println("IupacTree processH2po3 test : " + test);
//			System.out.println("IupacTree processH2po3 seq : " + seq);
			processedSeq = processedSeq.replaceAll("(?i)H2po3", "P");
			System.out.println("IupacTree processH2po3 seq : " + processedSeq);
		}
		catch(Exception ex)
		{
			System.err.println("IupacTree processH2po3 : " + ex.getMessage());
		}
		return processedSeq;
	}
	
	public String processS(String seq)
	{
		String processedSeq=seq;
		
		processedSeq = processedSeq.replaceAll("(?i)Hso3", "S");
		processedSeq = processedSeq.replaceAll("(?i)Hso4", "S");
		processedSeq = processedSeq.replaceAll("(?i)So3", "S");
		processedSeq = processedSeq.replaceAll("(?i)So4", "S");
		processedSeq = processedSeq.replaceAll("(?i)Su", "S");
		
		
		try
		{
			String regex = "(\\[?)(S)(-)([0-9|?])(\\]?)";
			processedSeq = processedSeq.replaceAll(regex, "[$2\\(?-$4\\)]");		
//			System.out.println("IupacTree processOpenLinkage test : " + test);
//			System.out.println("IupacTree processOpenLinkage seq : " + seq);
		}
		catch(Exception ex)
		{
			System.err.println("IupacTree processS : " + ex.getMessage());
		}
		
		processedSeq = processedSeq.replaceAll("(?i)S(?!\\()", "S(-?)");
		
//		System.out.println("IupacTree processP sequence : " + seq);
//		System.out.println("IupacTree processS : " + processedSeq);
		return processedSeq;
	}
	
	public String processF(String seq)
	{
		String processedSeq=seq;
//		System.out.println("IupacTree processF seq : " + processedSeq);
		try
		{
			String regex = "(\\()(\\d)(F)(\\))"; //(2F) -> [F(-2)] //"(\\[?)(F)(-)([0-9|?])(\\]?)";
			processedSeq = processedSeq.replaceAll(regex, "[$3\\(?-$2\\)]");		
//			System.out.println("IupacTree processOpenLinkage test : " + test);
//			System.out.println("IupacTree processF seq : " + processedSeq);
		}
		catch(Exception ex)
		{
			System.err.println("IupacTree processF : " + ex.getMessage());
		}
		
//		System.out.println("IupacTree processP sequence : " + seq);
//		System.out.println("IupacTree processS : " + processedSeq);
		return processedSeq;
	}
	

	public String processComposedResidue(String seq, String iupacRes, String expandedRes)
	{

		String processedSeq=seq;
			if(processedSeq.toUpperCase().contains(iupacRes.toUpperCase()))
			{
				processedSeq = processedSeq.replaceAll("(?i)"+iupacRes, expandedRes);
			}
			
		return processedSeq;
	}
		
	
	public String processComposedResidues(String seq)
	{
		String processedSeq=seq;

	

		processedSeq = processComposedResidue(processedSeq,"GalNAc", "[NAc(?1-2)]Gal");
		processedSeq = processComposedResidue(processedSeq,"GalN", "[N(?1-?)]Gal");
		
		processedSeq = processComposedResidue(processedSeq,"GlcNAc", "[NAc(?1-2)]Glc");
		processedSeq = processComposedResidue(processedSeq,"GlcN", "[N(?1-?)]Glc");
		
		processedSeq = processComposedResidue(processedSeq,"ManNAc", "[NAc(?1-2)]Man");
		processedSeq = processComposedResidue(processedSeq,"HexNAc", "[NAc(?1-2)]Hex");
		
		processedSeq = processComposedResidue(processedSeq,"NeuAc2", "[NAc(?1-?)][NAc(?1-?)]Kdn");
		processedSeq = processComposedResidue(processedSeq,"Neu4,5Ac2", "[NAc(?1-4)][NAc(?1-5)]Kdn");
		processedSeq = processComposedResidue(processedSeq,"Neu5,9Ac2", "[NAc(?1-5)][NAc(?1-9)]Kdn");
		processedSeq = processComposedResidue(processedSeq,"Neu5,?Ac2", "[NAc(?1-5)][NAc(?1-?)]Kdn");
		processedSeq = processComposedResidue(processedSeq,"Neu5,?,?Ac3", "[NAc(?1-5)][NAc(?1-?)][NAc(?1-?)]Kdn");
		processedSeq = processComposedResidue(processedSeq,"BacAc2", "[NAc(?1-4)][NAc(?1-2)]Bac");
			

		processedSeq = processComposedResidue(processedSeq,"NeuAc", "[NAc(?1-5)]Kdn");
		processedSeq = processComposedResidue(processedSeq,"Neu5Ac", "[NAc(?1-5)]Kdn");
		processedSeq = processComposedResidue(processedSeq,"NeuGc", "[NGc(?1-5)]Kdn");
		processedSeq = processComposedResidue(processedSeq,"Neu?c", "[NAc(?1-5)]Kdn");
		
		return processedSeq;
	}
	
	//---------------------------------------------
	
	public String processBac(String seq)
	{
		String bac = "{2,4-diacetamido-2,4,6-trideoxyhexose}";
		String processedSeq=seq;
//		if(TreeTools.isRepeatSequence(processedSeq))
//		{
			if(processedSeq.contains(bac))
			{
				processedSeq = processedSeq.replace(bac, "BacAc2");
//				System.out.println("IupacTree processBac "+ processedSeq);
			}
			
//		}
		return processedSeq;
	}
	
//	public String processCer(String seq)
//	{
//		String cer = "Cer";
//		String processedSeq=seq;
////		if(TreeTools.isRepeatSequence(processedSeq))
////		{
//			if(processedSeq.contains(cer))
//			{
//				processedSeq = processedSeq.replace(cer, "");
////				System.out.println("IupacTree processCer "+ processedSeq);
//			}
//			
////		}
//		return processedSeq;
//	}
	
	public void parseResidue() throws Exception
	{
		for(int j = 0;j<branches.size();j++)
		{
			branches.get(j).setResidues();
			branches.get(j).setCtLength();
			branches.get(j).setIupacLength();
		}
	}
	
	
	
//	public void parseResidue(int branchIdx) throws Exception
//	{
//		try
//		{
//			branches.get(branchIdx).setResidues();
//			branches.get(branchIdx).setCtLength();
//			branches.get(branchIdx).setIupacLength();
//		}
//		catch(Exception ex)
//		{
//			System.err.println("IupacTree parseResidue(int branchIdx)  " + ex.getMessage());	
//			throw ex;
//		}
//	}
	
	public void parseBranch()
	{
//		System.out.println("IupacTree.parse() sequence : " + sequence);
		IupacBranch b = new IupacBranch(this.sequence);	
		b.setLevel(0);
		b.setRank(0);
		b.setId(b.buildBranchId(b.getRank(), ""));
		b.setTree(this);
		branches.add(b);
		
		int i=0;
		
		do
		{			
		ArrayList<IupacBranch> branches_by_level = getBranches(i);
		IupacBranch branch;
		
		for(int j = 0;j<branches_by_level.size();j++)
			{
			branch = branches_by_level.get(j);
//			System.out.println("IupacTree.parse(): branch" + branch.toString());
			
				if(branch.isBranching())
				{
					branches.addAll(split(branch));
					branches.remove(branch);
				}
			}
		i++;
		}while(i<=getMaxLevel());
		
	}
	
	protected int getMaxLevel()
	{
		int maxLevel=0;
		for(int i =0; i<branches.size();i++)
		{
			if(branches.get(i).getLevel()>maxLevel)
			{
			maxLevel=branches.get(i).getLevel();
			}
		}
//		System.out.println("IupacTree getMaxLevel : " + maxLevel);
		return maxLevel;
	}
	
	protected ArrayList<IupacBranch> split(IupacBranch branch)
	{
		ArrayList<IupacBranch> subbranches = new ArrayList<IupacBranch>();
		// split in branches!!!
		if(!branch.isBranching())
		{
//			System.out.println("IupacTree split(branch) NOT Branching: " );
			subbranches.add(branch);
		}
		else
		{
//			System.out.println("IupacTree split(branch) Branching: " );
			subbranches.add(branch.getRootFromBranch());
			//add root to list with parent(), level, rank
			
			ArrayList<IupacBranch> b = branch.getBranches();
			for (int i = 0; i < b.size(); i++) {
//			    System.out.println(b.get(i));
			    subbranches.add(b.get(i));
			}
			
		}
		
		return subbranches;	
		
	//		String b_sequence = sequence; //shortened sequence for sub branch
	//		Branch b = new Branch(b_sequence) ;//new sub branch
	//		subbranches.add(b);//add to list
	}

	protected void printTree(ArrayList<IupacBranch> branches)
	{
		System.out.println("#################Tree#################");
		System.out.println("Initial sequence : " + originalSequence);
		System.out.println("Pre-processed sequence : " + sequence);
		System.out.println("Glycan type : " + glycanType);
		System.out.println("######################################");
		
		for(int i=0;i<branches.size();i++)
		{
//			System.out.println(branches.get(i).toString());
//			System.out.println("branch level: "+branches.get(i).getLevel()
//								+" rank: "+branches.get(i).getRank()
//								+" id: "+branches.get(i).getId()
//								+" parent_id: "+branches.get(i).getParentId()
//								+" iupacLength: "+branches.get(i).getIupacLength()
//								+" ctLength: "+branches.get(i).getCtLength()
//								+" seq: "+branches.get(i).getSequence()
//								+" tree: "+branches.get(i).getTree()
//								);
			
			for(int j=0 ;j<branches.get(i).getResiduesList().size();j++)
			{
//				System.out.println('\t' + "residues sequence: " + branches.get(i).getResiduesList().get(j).getSequence()
//								+ " rank: " + branches.get(i).getResiduesList().get(j).getRank()
//								+ " id: " + branches.get(i).getResiduesList().get(j).getId()
//								+ " branchId: " + branches.get(i).getResiduesList().get(j).getBranch().getId()
//								+ " abstractResidue: " + branches.get(i).getResiduesList().get(j).getAbstractResidue().getClass().getName()
//								+ " branch: " + branches.get(i).getResiduesList().get(j).getBranch()
//								);
			}
		}
	}
	
	protected String convertToCT() throws Exception
	{
		ArrayList<IupacBranch> branchesCT = null;
		
		String result = null;
		try
		{
			result="";
			//Expand the tree to split composed residues to single residues.
			//FIXME!! expand the iupac tree to CT tree.expand and dependencies
	//		expand(this.branches);
	//		System.err.println("IupacTree convertToCT Ctbranches.size() : " + Ctbranches.size());
	//		branchesCT = this.Ctbranches;
	//		System.err.println("IupacTree convertToCT branchesCT : "+ branchesCT);
			
			branchesCT = this.branches;
			//Sort numbering RES et LIN according to the traversal algo
			branchesCT = sort(branchesCT);
	//		System.err.println("IupacTree convertToCT branchesCT sorted");
			
			/*write sections*/
			
			//mandatory section RES
			result += getRES(branchesCT); 
			
			/*optional sections LIN and REP*/
	//		System.out.println("IupacTree convertToCT branches.size() : " + branchesSorted.size());
	//		System.out.println("IupacTree convertToCT branches.get(0).getResiduesList().size() : " +branchesSorted.get(0).getResiduesList().size());
			
			/*LIN does not exist if only one abstractResidue (-> no linkage) except if abstractResidue is composed*/
			boolean test1 = ((branchesCT.size()== 1) && (branchesCT.get(0).getResiduesList().size()==1));
			boolean test2 = branchesCT.get(0).getResiduesList().get(0).getAbstractResidue().isGenericComposedResidue();
			
			if(!test1 || test2)
			{
				result += getLIN(branchesCT); 
			}
			
			//REP if branch contains a repetition
			if(repeats.size()>0)
			{
				result += getREP();
			}
					
			if(undcaps.size()>0)
			{
				
				result += getUND();
			}
		}
		catch(Exception ex)
		{
			result=null;
			System.err.println("IupacTree convertToCT()" + result);
		}
		return result;
	}
	
//	protected void expand(ArrayList<IupacBranch> branchesToExpand)throws Exception
//	{
//		ArrayList<IupacBranch> branchesExpanded = new ArrayList<IupacBranch>();
//		IupacBranch branch = null; 
//		try
//		{
//		//TODO
//			//foreach branchesToExtand(Iupac)
//			for(int i=0;i<branchesToExpand.size();i++)
//			{	
//			branch = branchesToExpand.get(i);
//			
//					System.out.println("IupacTree Expand branch : " + i + " / " + branch.getId() + " hasComposedResidue : " + branch.hasComposedResidue());
////					branchesToExpand.get(i).toString();
////					System.err.println("Children : " + TreeTools.getChildrenBranchIds(branchesToExpand.get(i), branchesToExpand)); 
////					IupacBranch parent = TreeTools.getParentBranch(branchesToExpand.get(i), branchesToExpand);
////					if(parent!=null)
////					System.err.println("Parent : " + parent.getId()); 
//			
////					System.err.println("hasComposedResidue : " + branchesToExpand.get(i).hasComposedResidue());
//			//create a new branch branchesExpanded(GlycoCT) with branch_id
//				//for each residue
//				//take the abstractresidue
//				//if (!composedResidue)
//					//add residue with abstract residue to the branch
//				//else
//					//add monsaccharide to the branch
//						//for each substituent
//							//add new branch with residue and substituent
//						//
//			
//			branch.expand(branchesToExpand);
//			Ctbranches.addAll(branch.getCtBranches());
//			}
//			
//		}
//		catch(Exception ex)
//		{
//			System.err.println("IupacTree expand error : " + ex.getMessage());
//			ex.printStackTrace();
//			throw ex;
//		}
////		return branchesExpanded;
//	}
	
	
	protected ArrayList<IupacBranch> sort(ArrayList<IupacBranch> branchesToSort)throws Exception
	{
		try
		{
			CtSorter sorter = new CtSorter(branchesToSort);
//			System.err.println("IupacTree sort new CtSorter " + branchesToSort);
			sorter.sortBranches();
//			System.err.println("IupacTree sort sortBranches " + branchesToSort);
			CtSortedbranches = sorter.getBranchesSorted();
//			System.err.println("IupacTree sort getBranchesSorted : " + CtSortedbranches);
		}
		catch(Exception ex)
		{
			System.err.println("IupacTree sort  " + ex.getMessage());	
			throw ex;
		}
		return CtSortedbranches;
	}
	
	protected String getRES(ArrayList<IupacBranch> branchesSorted) throws Exception
	{
//		int nb = 0;
//		int resIdx = TreeTools.getTopTreeRepeat(this).getResIdx();
		int resIdx;// =  TreeTools.getTopTreeRepeat(this).getLinIdx();
		IupacTree parent = null;
		if(this.isUndCapTree())
		{
			parent = ((IupacUndCapTree)this).getParentTree();
			resIdx = parent.getResIdx();
		}
		else
		{
			resIdx =  TreeTools.getTopTreeRepeat(this).getResIdx();
		}	
		
		
		String result = "RES"+ Constants.EOL;
		for (int i = 0;i < branchesSorted.size();i++)
		{
			for(int j = 0; j< branchesSorted.get(i).getResiduesList().size();j++)
			{
				try
				{
					resIdx++;
					//System.out.println("nb"+ nb);
					AbstractResidue absRes = branchesSorted.get(i).getResiduesList().get(j).getAbstractResidue();
					
					//test if substituent, monosach or composed.... and decompose
					if(absRes.isGenericMonosaccharideResidue())
					{
						absRes.setResNb(resIdx);
						result += ((GenericMonosaccharideResidue)absRes).getCTvalue()+ Constants.EOL;
					}
					if(absRes.isGenericRepeatResidue())
					{
						absRes.setResNb(resIdx);
						result += ((GenericRepeatResidue)absRes).getCTvalue()+ Constants.EOL;
					}
					
					if(absRes.isGenericSubstituentResidue())
					{
						absRes.setResNb(resIdx);
						result += ((GenericSubstituentResidue)absRes).getCTvalue()+ Constants.EOL;
					}
					if(absRes.isGenericComposedResidue())
					{
						GenericMonosaccharideResidue mon = ((GenericComposedResidue)absRes).getMonosaccharide();
						mon.setResNb(resIdx);
						result += ((GenericComposedResidue)absRes).getMonosaccharide().getCTvalue()+ Constants.EOL;
						
						ArrayList<GenericSubstituentResidue> subs = ((GenericComposedResidue)absRes).getSubstituents(); 
						for(int k=0;k<subs.size();k++)
						{
							resIdx++;
							GenericSubstituentResidue sub = subs.get(k);
							sub.setResNb(resIdx);
							//((GenericComposedResidue)absRes).addToSubstituents(sub);
							result += sub.getCTvalue()+ Constants.EOL;
						}
					}
//					System.out.println("IupacTree getRES for "+branchesSorted.get(i).getResiduesList().get(j).toString() );	
				}
				catch(Exception ex)
				{
					System.err.println("IupacTree getRES for "+branchesSorted.get(i).getResiduesList().get(j).toString() + " " + ex.getMessage());
//					ex.printStackTrace();
					throw ex;
				}
				//System.out.println("IupacTree getRES " + i + "/"+ j + " : " + absRes.getClass().getName());
			}
		}
		TreeTools.getTopTreeRepeat(this).setResIdx(resIdx);
		
		//for each undcaps tree, set the initial resIdx value
		if(!this.isUndCapTree())
		{
//			System.out.println("IupacTree getRES MAIN TREE setLinIdx " + resIdx +" "+ this.toString() );
			setResIdx(resIdx);
		}		
		if(this.isUndCapTree())
		{
			parent.setResIdx(resIdx);
//			System.out.println("IupacTree getRES UND TREE : resIdx " + resIdx + ", getResIdx() " + getResIdx()+ " parent: " + parent.toString() + " parent idx "+parent.getResIdx());			
		}		
		
		return result;
	}
	
	
	protected String getLIN(ArrayList<IupacBranch> branchesSorted) throws Exception
	{
		String result = "LIN"+ Constants.EOL;		
		int linNb;// =  TreeTools.getTopTreeRepeat(this).getLinIdx();
		IupacTree parent = null;
		if(this.isUndCapTree())
		{
			parent = ((IupacUndCapTree)this).getParentTree();
			linNb = parent.getLinIdx();
		}
		else
		{
			linNb =  TreeTools.getTopTreeRepeat(this).getLinIdx();
		}	
			
//		System.out.println("IupacTree getLIN tree top start "+ TreeTools.getTopTreeRepeat(this).getLinIdx() + " " + TreeTools.getTopTreeRepeat(this).toString());
		
		for (int i = 0;i < branchesSorted.size();i++)
		{
			for(int j = 0; j< branchesSorted.get(i).getResiduesList().size();j++)
			{
				try
				{
					AbstractResidue absRes = branchesSorted.get(i).getResiduesList().get(j).getAbstractResidue();
					Link l = null;
					if(absRes.isGenericMonosaccharideResidue())
					{
						l = absRes.getLinkToPrevious();
						if(l!=null)
						{								
							linNb++;
							l.setLinNb(linNb);
							l.setRes2CtNumber(absRes.getResNb());
							
							AbstractResidue prevResidue = TreeTools.getPreviousResidue(i, j, branchesSorted).getAbstractResidue();
							prevResidue = TreeTools.getPreviousResidue(branchesSorted.get(i).getResiduesList().get(j)).getAbstractResidue();							
							if(prevResidue.isGenericComposedResidue())
							{
								l.setRes1CtNumber(((GenericComposedResidue)prevResidue).getMonosaccharide().getResNb());
								
							}
							else
							{
								l.setRes1CtNumber(prevResidue.getResNb());
							}	
							
							
							result += l.getCTvalue()+ Constants.EOL;
						}
					}
					/////////////////////////////////////////////////////////////
					
					if(absRes.isGenericRepeatResidue())
					{
						l = absRes.getLinkToPrevious();
						if(l!=null)
						{	
							
							linNb++;
							l.setLinNb(linNb);
							l.setRes2CtNumber(absRes.getResNb());
							
							AbstractResidue prevResidue = TreeTools.getPreviousResidue(i, j, branchesSorted).getAbstractResidue();
							prevResidue = TreeTools.getPreviousResidue(branchesSorted.get(i).getResiduesList().get(j)).getAbstractResidue();
							
							if(prevResidue.isGenericComposedResidue())
							{
								l.setRes1CtNumber(((GenericComposedResidue)prevResidue).getMonosaccharide().getResNb());								
							}
							else
							{
								l.setRes1CtNumber(prevResidue.getResNb());
							}
							
							result += l.getCTvalue()+ Constants.EOL;
						}
					}					
					/////////////////////////////////////////////////////////////
					
					
					if(absRes.isGenericSubstituentResidue())
					{
						l = absRes.getLinkToPrevious();
						if(l!=null)
						{
							linNb++;
							l.setLinNb(linNb);
							l.setRes2CtNumber(absRes.getResNb());
							
							
							AbstractResidue prevResidue = TreeTools.getPreviousResidue(i, j, branchesSorted).getAbstractResidue();
							if(prevResidue.isGenericComposedResidue())
							{
								l.setRes1CtNumber(((GenericComposedResidue)prevResidue).getMonosaccharide().getResNb());
							}
							else
							{
								l.setRes1CtNumber(prevResidue.getResNb());
							}
							
//							l.setRes1CtNumber(getPreviousResidue(i, j).getResidue().getResNb());
							result += l.getCTvalue()+ Constants.EOL;
						}
					}
					
					if(absRes.isGenericComposedResidue())
					{
						GenericMonosaccharideResidue mon = ((GenericComposedResidue)absRes).getMonosaccharide();
						l = mon.getLinkToPrevious();
						if(l!=null)
						{
							linNb++;
							l.setLinNb(linNb);
							l.setRes2CtNumber(mon.getResNb());
							
							AbstractResidue prevResidue = TreeTools.getPreviousResidue(i, j, branchesSorted).getAbstractResidue();
							if(prevResidue.isGenericComposedResidue())
							{
								l.setRes1CtNumber(((GenericComposedResidue)prevResidue).getMonosaccharide().getResNb());
							}
							else
							{
								l.setRes1CtNumber(prevResidue.getResNb());
							}	
							result += ((GenericComposedResidue)absRes).getMonosaccharide().getLinkToPrevious().getCTvalue()+ Constants.EOL;
						}

						Link l2 = null;
						ArrayList<GenericSubstituentResidue> subs = ((GenericComposedResidue)absRes).getSubstituents();
						for(int n=0;n<subs.size();n++)
						{
							l2 = subs.get(n).getLinkToPrevious();
							
							if(l2!=null)
							{
								linNb++;
								l2.setLinNb(linNb);
								l2.setRes1CtNumber(mon.getResNb());
								l2.setRes2CtNumber(subs.get(n).getResNb());
//								System.out.println(subs.get(n).toString() );								
								result += l2.getCTvalue()+ Constants.EOL;
							}
						}
						
					}

				}
				catch(Exception ex)
				{
				System.err.println("IupacTree getLIN for "+branchesSorted.get(i).getResiduesList().get(j).toString() + " " + ex.getMessage());	
				throw ex;
				}
			}
		}
		TreeTools.getTopTreeRepeat(this).setLinIdx(linNb);
//		System.out.println("IupacTree getLIN tree end "+ getLinIdx() + " " + this.toString() );
		
		if(!this.isUndCapTree())
		{
//			System.out.println("IupacTree getLIN MAIN TREE setLinIdx " + linNb +" "+ this.toString() );
			setLinIdx(linNb);
		}		
		if(this.isUndCapTree())
		{
			parent.setLinIdx(linNb);
//			System.out.println("IupacTree getLIN UND TREE : linNb " + linNb + ", getLinIdx() " + getLinIdx()+ " parent: " + parent.toString() + " parent idx "+parent.getLinIdx());
			
		}
//		System.err.println("IupacTree getLIN UND " + getLinIdx() + " " + toString());
		
		return result;
	}
	
	protected String getREP() throws Exception
	{
		
		
		String res = "REP" + Constants.EOL;
		res += parseRepeats(); 
		return res;
	}
	
	protected String getUND() throws Exception
	{
		
		
		String res = "UND" + Constants.EOL;
		res += parseUndCaps(); 
		return res;
	}
	
	protected String parseUndCaps() throws Exception
	{
		String result="";
		
		String unknown= Constants.UNKNOWN_LINKAGE_POS;
		
		for(int i=0;i<undcaps.size();i++)
		{
			String body="";
			String header="";
			
			IupacUndCapTree und = undcaps.get(i);
			
			
			und.parse();
			und.addParents(TreeTools.getAllMonosaccharides(this));
			
			body += und.convertToCT();
//			Link link = new Link(res1LinkagePosition, res2LinkagePosition)
			
			//FIXME
			header += "UND"+(i+1)+":100.0:100.0";
			header += Constants.EOL;
			header += "ParentIDs:" + und.getParentIds();
			header += Constants.EOL;
			header += "SubtreeLinkageID1:" 
						+ und.getRootLink().getUndCTvalue()
//								+ "o" 
//								+ Constants.openingParenthesis 
////								+ und.getRootLink().substring(1,2)
//								+ und.getRootLinkage().substring(1,2)
//								+ Constants.LINKAGE_SEPARATOR 
////								+ und.getRootLink().substring(3,4)
//								+ und.getRootLinkage().substring(3,4)
//								+ Constants.closingParenthesis 
//								+ "n"
								; 

			header += Constants.EOL;			
			
		 result += (header + body);
		}
		return result;
	}
	
	protected String parseRepeats() throws Exception
	{
		String result="";
		
		String unknown= Constants.UNKNOWN_LINKAGE_POS;
		
		for(int i=0;i<repeats.size();i++)
		{
			String body="";
			String header="";
			
			IupacRepeatTree rep = repeats.get(i);
			rep.parse();
			body += rep.convertToCT();
			
			
			header += "REP"+(i+1)+":";
			header += TreeTools.getLastSubtreeResidue(rep).getAbstractResidue().getResNb();
			//FIXME start
			header += Constants.UNKNOWN; //Not known from the iupac sequence
			header += Constants.openingParenthesis + unknown + "+" +unknown + Constants.closingParenthesis;
			header += TreeTools.getFirstSubtreeResidue(rep).getAbstractResidue().getResNb();
			header += Constants.UNKNOWN;
			//FIXME end
			header += "=";
			header += rep.getMultitudeMin()+Constants.DASH+rep.getMultitudeMax();
			header += Constants.EOL;
			
			
		 result += (header + body);
		}
		return result;
	}
	
	protected int countIupacResidue()
	{
		int nb =0;
		// for each abstractResidue of each branch give a number according to the abstractResidue comparison algorithm
		for (int idxBranch=0; idxBranch < branches.size() ; idxBranch++)
		{
			for(int idxResidue=0; idxResidue < branches.get(idxBranch).getResiduesList().size(); idxResidue++)
			{
				nb++;
			}
		}
//		System.out.println("IupacTree countIupacResidue() : " + nb);
		return nb;		
	}
	
	protected int countCtResidue()
	{
		int nb =0;
		
		// for each abstractResidue of each branch give a number according to the abstractResidue comparison algorithm
		for (int idxBranch=0; idxBranch < branches.size() ; idxBranch++)
		{
			for(int idxResidue=0; idxResidue < branches.get(idxBranch).getResiduesList().size(); idxResidue++)
			{
				nb++;
				if(branches.get(idxBranch).getResiduesList().get(idxResidue).getAbstractResidue().isGenericComposedResidue())
				{
					nb++;
				}
			}
		}
//		System.out.println("IupacTree countCtResidue() : " + nb);
		return nb;		
	}
	
	public void printBranchesIds(ArrayList<IupacBranch> branches)
	{
		
	for(int i =0 ; i<branches.size();i++)
		{
//			System.out.println("IupacTree getBranchesIds() : " + i + " : " + branches.get(i).getId());
		}
	}
	
	public boolean hasPLink(String seq)// throws Exception
	{
		boolean res = false;
		System.err.println("IupacTree hasPLink : " + seq);
		if(hasPTerminal(seq) || hasPInternal(seq))
		//if(seq.contains("P"))
		{
			res = true;
//			System.out.println("IupacTree hasPLink :" + seq + " has P : " + res);
		}		
		return res;
	}
	
	public boolean hasPTerminal(String seq)
	{
		boolean res = false;
		
		if(seq.contains(Constants.P_TERMINAL) && seq.charAt(seq.length()-2)==Constants.P)
		{
			res = true;
//			System.out.println("IupacTree hasPTerminal : " + seq + " / " + Constants.P_TERMINAL + " " + res );
			//throw new Exception("IupacTree hasPTerminal : " + seq );
		}
//		System.out.println("IupacTree hasPTerminal : " + res );
		return res;
	}
	
	public boolean hasPInternal(String seq)
	{
		boolean res = false;
		
		try
		{
			if(seq.contains(Constants.P_INTERNAL))
			{
				res = true;
//				System.out.println("IupacTree hasPInternal : " + seq + " / " + Constants.P_INTERNAL + " " + res );
				//throw new Exception("IupacTree hasPInternal : " + seq );
			}
			
		}
		catch(Exception ex)
		{
			System.err.println("IupacTree hasPInternal seq error :" + seq +" " + ex.getMessage());
		}
//		System.out.println("IupacTree hasPInternal : " + res );
		return res;
	}
	
	
	public boolean isUndCapTree()
	{
		boolean result = false;
		IupacUndCapTree c = new IupacUndCapTree();
		
		if(c.getClass().isAssignableFrom(this.getClass()))
		{
			result=true;
//			System.out.println("AbstractResidue : isGenericComposedResidue " + result);
		}
		return result;
	}
}	
